home *** CD-ROM | disk | FTP | other *** search
- /*
- * devXylogics450.c --
- *
- * Driver the for Xylogics 450 SMD controller.
- * The technical
- * manual to refer to is "XYLOGICS 450 Disk Controller User's Manual".
- * (Date Aug 1983, Rev. B)
- *
- * Copyright 1989 Regents of the University of California
- * Permission to use, copy, modify, and distribute this
- * software and its documentation for any purpose and without
- * fee is hereby granted, provided that the above copyright
- * notice appear in all copies. The University of California
- * makes no representations about the suitability of this
- * software for any purpose. It is provided "as is" without
- * express or implied warranty.
- */
-
- #ifndef lint
- static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/dev/sun4.md/devXylogics450.c,v 9.7 91/09/10 18:20:16 rab Exp $ SPRITE (Berkeley)";
- #endif /* not lint */
-
- #include "sprite.h"
- #include "mach.h"
- #include "dev.h"
- #include "devInt.h"
- #include "devDiskLabel.h"
- #include "dbg.h"
- #include "vm.h"
- #include "sys.h"
- #include "sync.h"
- #include "fs.h"
- #include "vmMach.h"
- #include "devQueue.h"
- #include "devBlockDevice.h"
- #include "xylogics450.h"
- #include "devDiskStats.h"
- #include "stdio.h"
- #include "stdlib.h"
- #include "bstring.h"
- /*
- * RANDOM NOTES:
- *
- * The Xylogics board does crazed address relocation, either to 20-bit
- * or 24-bit addresses. I expect only 24-bit mode with the sun3's,
- * which means the top half of the DVMA addresses has to be cropped
- * off and put in the relocation register part of the IOPB
- *
- * The IOPB format must be byte swapped with respect to the documentation
- * because of disagreement between the multibus/8086 ordering and the
- * sun3/Motorola ordering.
- */
-
- /*
- * The I/O Registers of the 450 are used to initiate commands and to
- * specify where parameter blocks (IOPB) are. The bytes are swapped
- * because the controller thinks it's on a multibus.
- */
- typedef struct XylogicsRegs {
- char relocHigh; /* Byte 1 - IOPB Relocation Register High Byte */
- char relocLow; /* Byte 0 - IOPB Relocation Register Low Byte */
- char addrHigh; /* Byte 3 - IOPB Address Register High Byte */
- char addrLow; /* Byte 2 - IOPB Address Register Low Byte */
- char resetUpdate; /* Byte 5 - Controller Reset/Update Register */
- char status; /* Byte 4 - Controller Status Register (CSR) */
- } XylogicsRegs;
-
- /*
- * Definitions for the bits in the status register
- * XY_GO_BUSY - set by driver to start command, remains set
- * until the command completes.
- * XY_ERROR - set by the controller upon error
- * Do error reset by setting this bit to 1
- * XY_DBL_ERROR - set by controller upon error DMA'ing status bytes.
- * XY_INTR_PENDING - set by controller, must be unset by handler
- * by writing a 1 to it.
- * XY_20_OR_24 - If zero, the controller does 20 bit relocation,
- * otherwise it uses the relocation bytes as the
- * top 16 bits of the DMA address.
- * XY_ATTN_REQ - Set when you want to add more IOPB's to the chain,
- * clear this when work on the chain is complete.
- * XY_ATTN_ACK - Set by the controller when it's safe to add/delete
- * IOPB's to the command chain.
- * XY_READY - "Indicates the Ready-On Cylinder status of the
- * last drive selected.
- */
- #define XY_GO_BUSY 0x80
- #define XY_ERROR 0x40
- #define XY_DBL_ERROR 0x20
- #define XY_INTR_PENDING 0x10
- #define XY_20_OR_24 0x08
- #define XY_ATTN_REQ 0x04
- #define XY_ATTN_ACK 0x02
- #define XY_READY 0x01
-
- typedef struct XylogicsIOPB {
- /*
- * Byte 1 - Interrupt Mode
- */
- unsigned char :1;
- unsigned char intrIOPB :1; /* Interrupt upon completion of IOPB */
- unsigned char intrError :1; /* Interrupt upon error (440 only) */
- unsigned char holdDualPort :1; /* Don't release dual port drive */
- unsigned char autoSeekRetry :1; /* Enables re-calibration on error */
- unsigned char enableExtras :1; /* Enables commands 3 and 4 */
- unsigned char eccMode :2; /* ECC Correction Mode, set to 2 */
- /*
- * Byte 0 - Command Byte
- */
- unsigned char autoUpdate :1; /* Update IOPB after completion */
- unsigned char relocation :1; /* Enables relocation on data addrs */
- unsigned char doChaining :1; /* Enables chaining of IOPBs */
- unsigned char interrupt :1; /* When set interupts upon completion */
- unsigned char command :4; /* Commands defined below */
- /*
- * Byte 3 - Status Byte 2
- */
- unsigned char errorCode; /* Error codes, 0 means success, the rest
- * of the codes are explained in the manual */
- /*
- * Byte 2 - Status Byte 1
- */
- unsigned char error :1; /* Indicates an error occurred */
- unsigned char :2;
- unsigned char cntrlType :3; /* Controller type, 1 = 450 */
- unsigned char :1;
- unsigned char done :1; /* Command is complete */
- /*
- * Byte 5 - Drive Type, Unit Select
- */
- unsigned char driveType :2; /* 2 => Fujitsu 2351 (Eagle) */
- unsigned char :4;
- unsigned char unit :2; /* Up to 4 drives per controller */
- /*
- * Byte 4 - Throttle
- */
- unsigned char transferMode :1; /* == 0 for words, 1 for bytes */
- unsigned char interleave :4; /* == 0 for 1:1 interleave */
- unsigned char throttle :3; /* 4 => 32 words per DMA burst */
-
- unsigned char sector; /* Byte 7 - Sector Byte */
- unsigned char head; /* Byte 6 - Head Byte */
- unsigned char cylHigh; /* Byte 9 - High byte of cylinder address */
- unsigned char cylLow; /* Byte 8 - Low byte of cylinder address */
- unsigned char numSectHigh; /* Byte B - High byte of sector count */
- unsigned char numSectLow; /* Byte A - Low byte of sector count.
- * This byte is also used to return status
- * with the Read Drive Status command */
- /*
- * Don't byteswap the data address and relocation offset. All the
- * byte-swapped device is going to do is turn around and put these
- * addresses back onto the bus, so don't have to worry about ordering.
- * (The relocation register is scary, but the Sun MMU puts all the
- * DMA buffer space into low physical memory addresses, so the relocation
- * register is probably zero anyway.)
- */
- unsigned char dataAddrHigh; /* Byte D - High byte of data address */
- unsigned char dataAddrLow; /* Byte C - Low byte of data address */
- unsigned char relocHigh; /* Byte F - High byte of relocation value */
- unsigned char relocLow; /* Byte E - Low byte of relocation value */
- /*
- * Back to byte-swapping
- */
- unsigned char reserved1; /* Byte 11 */
- unsigned char headOffset; /* Byte 10 */
- unsigned char nextHigh; /* Byte 13 - High byte of next IOPB address */
- unsigned char nextLow; /* Byte 12 - Low byte of next IOPB address */
- unsigned char eccByte15; /* Byte 15 - ECC Pattern byte 15 */
- unsigned char eccByte14; /* Byte 14 - ECC Pattern byte 14 */
- unsigned char eccAddrHigh; /* Byte 17 - High byte of sector bit address */
- unsigned char eccAddrLow; /* Byte 16 - Low byte of sector bit address */
- } XylogicsIOPB;
-
- #define XYLOGICS_MAX_CONTROLLERS 2
- #define XYLOGICS_MAX_DISKS 4
-
- /*
- * Defines for the command field of Byte 0. These are explained in
- * pages 25 to 58 of the manual. The code here uses READ and WRITE, of course,
- * and also XY_RAW_READ to learn the proper drive type, and XY_READ_STATUS
- * to see if a drive exists.
- */
- #define XY_NO_OP 0x0
- #define XY_WRITE 0x1
- #define XY_READ 0x2
- #define XY_WRITE_HEADER 0x3
- #define XY_READ_HEADER 0x4
- #define XY_SEEK 0x5
- #define XY_DRIVE_RESET 0x6
- #define XY_WRITE_FORMAT 0x7
- #define XY_RAW_READ 0x8
- #define XY_READ_STATUS 0x9
- #define XY_RAW_WRITE 0xA
- #define XY_SET_DRIVE_SIZE 0xB
- #define XY_SELF_TEST 0xC
- /* reserved 0xD */
- #define XY_BUFFER_LOAD 0xE
- #define XY_BUFFER_DUMP 0xF
-
- /*
- * Defines for error code values. They are explained fully in the manual.
- */
- #define XY_NO_ERROR 0x00
- /*
- * Programming errors
- */
- #define XY_ERR_INTR_PENDING 0x01
- #define XY_ERR_BUSY_CONFLICT 0x03
- #define XY_ERR_BAD_CYLINDER 0x07
- #define XY_ERR_BAD_SECTOR 0x0A
- #define XY_ERR_BAD_COMMAND 0x15
- #define XY_ERR_ZERO_COUNT 0x17
- #define XY_ERR_BAD_SECTOR_SIZE 0x19
- #define XY_ERR_SELF_TEST_A 0x1A
- #define XY_ERR_SELF_TEST_B 0x1B
- #define XY_ERR_SELF_TEST_C 0x1C
- #define XY_ERR_BAD_HEAD 0x20
- #define XY_ERR_SLIP_SECTOR 0x09
- #define XY_ERR_SLAVE_ACK 0x0E
- /*
- * Soft errors that may be recovered by retrying. Retry at most twice.
- */
- #define XY_SOFT_ERR_TIME_OUT 0x04
- #define XY_SOFT_ERR_BAD_HEADER 0x05
- #define XY_SOFT_ERR_ECC 0x06
- #define XY_SOFT_ERR_NOT_READY 0x16
- /*
- * These errors cause a drive re-calibration, then you retry the transfer.
- */
- #define XY_SOFT_ERR_HEADER 0x12
- #define XY_SOFT_ERR_FAULT 0x18
- #define XY_SOFT_ERR_SEEK 0x25
- /*
- * Errors during formatting.
- */
- #define XY_FORMAT_ERR_RUNT 0x0D
- #define XY_FORMAT_ERR_BAD_SIZE 0x13
- /*
- * Noteworthy errors.
- */
- #define XY_WRITE_PROTECT_ON 0x14
- #define XY_SOFT_ECC_RECOVERED 0x1F
- #define XY_SOFT_ECC 0x1E
-
- /*
- * Bit values for the numSectLow byte used to return Read Drive Status
- * XY_ON_CYLINDER == 0 if drive is not seeking
- * XY_DISK_READY == 0 if drive is ready
- * XY_WRITE_PROTECT == 1 if write protect is on
- * XY_DUAL_PORT_BUSY == 1 if dual ported drive is busy
- * XY_HARD_SEEK_ERROR == 1 if the drive reports a seek error
- * XY_DISK_FAULT == 1 if the drive reports any kind of fault
- */
- #define XY_ON_CYLINDER 0x80
- #define XY_DISK_READY 0x40
- #define XY_WRITE_PROTECT 0x20
- #define XY_DUAL_PORT_BUSY 0x10
- #define XY_HARD_SEEK_ERROR 0x80
- #define XY_DISK_FAULT 0x40
-
- /*
- * The XY_RAW_READ and XY_RAW_WRITE commands return sector header
- * information that looks like the following struct. Again, this
- * is byte-swapped in comparison with the documentation.
- *
- * A raw read is done during boot strap to determine the drive type.
- * The label is read at the same time to determine the disk geometry,
- * and this information is passed back into the controller.
- */
- typedef struct XylogicsSectorHeader {
- /*
- * Byte 1
- */
- char sectorHigh :2;
- char reserved :3;
- char cylHigh :3;
- /*
- * Byte 0
- */
- char cylLow :8;
- /*
- * Byte 3
- */
- char driveType :2;
- char sectorLow :6;
- /*
- * Byte 2
- */
- char head :8;
- } XylogicsSectorHeader;
-
-
- typedef struct XylogicsDisk XylogicsDisk;
- typedef struct Request Request;
-
- typedef struct XylogicsController {
- int magic; /* To catch bad pointers */
- Boolean busy; /* TRUE if the controller is busy. */
- volatile XylogicsRegs *regsPtr; /* Pointer to Controller's registers */
- int number; /* Controller number, 0, 1 ... */
- Request *requestPtr; /* Current active request. */
- Address dmaBuffer; /* Address of the DMA buffer
- * for reads/writes */
- int dmaBufferSize; /* Size of the dmaBuffer mapped. */
- volatile XylogicsIOPB *IOPBPtr; /* Ref to IOPB */
- Sync_Semaphore mutex; /* Mutex for queue access */
- Sync_Condition specialCmdWait; /* Condition to wait of for special
- * commands liked test unit ready and
- * reading labels. */
- int numSpecialWaiting; /* Number of processes waiting to
- * get access to the controller for
- * a sync command. */
- DevCtrlQueues ctrlQueues; /* Queues of disk attached to the
- * controller. */
- XylogicsDisk *disks[XYLOGICS_MAX_DISKS]; /* Disk attached to the
- * controller. */
- } XylogicsController;
-
- #define XY_CNTRLR_STATE_MAGIC 0xf5e4d3c2
-
-
- struct XylogicsDisk {
- int magic; /* Check against bad pointers */
- XylogicsController *xyPtr; /* Back pointer to controller state */
- int xyDriveType; /* Xylogics code for disk */
- int slaveID; /* Drive number */
- int numCylinders; /* ... on the disk */
- int numHeads; /* ... per cylinder */
- int numSectors; /* ... on each track */
- DevQueue queue;
- DevDiskMap map[DEV_NUM_DISK_PARTS];/* partitions */
- DevDiskStats *diskStatsPtr;
- };
-
- #define XY_DISK_STATE_MAGIC 0xa1b2c3d4
-
- /*
- * The interface to XylogicsDisk the outside world views the disk as a
- * partitioned disk.
- */
- typedef struct PartitionDisk {
- DevBlockDeviceHandle handle; /* Must be FIRST field. */
- int partition; /* Partition number on disk. */
- XylogicsDisk *diskPtr; /* Real disk. */
- } PartitionDisk;
-
- /*
- * Format of request queued for a Xylogics disk. This request is
- * built in the ctrlData area of the DevBlockDeviceRequest.
- */
-
- struct Request {
- List_Links queueLinks; /* For the dev queue modole. */
- int command; /* XY_READ or XY_WRITE. */
- XylogicsDisk *diskPtr; /* Target disk for request. */
- Dev_DiskAddr diskAddress; /* Starting address of request. */
- int numSectors; /* Number of sectors to transfer. */
- Address buffer; /* Memory to transfer to/from. */
- int retries; /* Number of retries on the command. */
- DevBlockDeviceRequest *requestPtr; /* Block device generating this
- * request. */
- };
-
- /*
- * State for each Xylogics controller.
- */
- static XylogicsController *xylogics[XYLOGICS_MAX_CONTROLLERS];
-
- /*
- * This controlls the time spent busy waiting for the transfer completion
- * when not in interrupt mode.
- */
- #define XYLOGICS_WAIT_LENGTH 250000
-
- /*
- * SECTORS_PER_BLOCK
- */
- #define SECTORS_PER_BLOCK (FS_BLOCK_SIZE / DEV_BYTES_PER_SECTOR)
-
- /*
- * Forward declarations.
- */
-
- static void ResetController();
- static ReturnStatus TestDisk();
- static ReturnStatus ReadDiskLabel();
- static void SetupIOPB();
- static ReturnStatus SendCommand();
- static ReturnStatus GetStatus();
- static ReturnStatus WaitForCondition();
- static void RequestDone();
- static void StartNextRequest();
- static void FillInDiskTransfer();
-
-
- /*
- *----------------------------------------------------------------------
- *
- * xyEntryAvailProc --
- *
- * Act upon an entry becomming available in the queue for a
- * device.. This routine is the Dev_Queue callback function that
- * is called whenever work becomes available for a device.
- * If the controller is not already busy we dequeue and start the
- * request.
- *
- * Results:
- * TRUE if the request is processed. FALSE if the request should be
- * enqueued.
- *
- * Side effects:
- * Request may be dequeue and submitted to the device. Request callback
- * function may be called.
- *
- *----------------------------------------------------------------------
- */
-
- static Boolean
- xyEntryAvailProc(clientData, newRequestPtr)
- ClientData clientData; /* Really the Device this request ready. */
- List_Links *newRequestPtr; /* The new request. */
- {
- register XylogicsDisk *diskPtr = (XylogicsDisk *) clientData ;
- XylogicsController *xyPtr = diskPtr->xyPtr;
- register Request *requestPtr = (Request *) newRequestPtr;
- ReturnStatus status;
-
- if (xyPtr->busy) {
- return FALSE;
- }
- status = SUCCESS;
- if (requestPtr->numSectors > 0) {
- status = SendCommand(diskPtr, requestPtr, FALSE);
- }
- if (status != SUCCESS) {
- RequestDone(requestPtr->diskPtr, requestPtr, status, 0);
- }
- return TRUE;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * DevXylogics450Init --
- *
- * Initialize a Xylogics controller.
- *
- * Results:
- * A NIL pointer if the controller does not exists. Otherwise a pointer
- * the the XylogicsController stucture.
- *
- * Side effects:
- * Map the controller into kernel virtual space.
- * Allocate buffer space associated with the controller.
- * Do a hardware reset of the controller.
- *
- *----------------------------------------------------------------------
- */
- ClientData
- DevXylogics450Init(cntrlrPtr)
- DevConfigController *cntrlrPtr; /* Config info for the controller */
- {
- XylogicsController *xyPtr; /* Xylogics specific state */
- register volatile XylogicsRegs *regsPtr;/* Control registers for Xylogics */
- char x; /* Used when probing the controller */
- int i;
- ReturnStatus status;
- Address addr;
-
-
- /*
- * Poke at the controller's registers to see if it works
- * or we get a bus error.
- */
- regsPtr = (volatile XylogicsRegs *) cntrlrPtr->address;
- status = Mach_Probe(sizeof(regsPtr->resetUpdate),
- (char *) &(regsPtr->resetUpdate), (char *) &x);
- if (status != SUCCESS) {
- return DEV_NO_CONTROLLER;
- }
- status = Mach_Probe(sizeof(regsPtr->addrLow), "x",
- (char *) &(regsPtr->addrLow));
- if (status == SUCCESS) {
- status = Mach_Probe(sizeof(regsPtr->addrLow),
- (char *) &(regsPtr->addrLow), &x);
- }
- if (status != SUCCESS || (x != 'x') ) {
- return DEV_NO_CONTROLLER;
- }
-
- /*
- * Allocate and initialize the controller state info.
- */
- xyPtr = (XylogicsController *)malloc(sizeof(XylogicsController));
- bzero((char *) xyPtr, sizeof(XylogicsController));
- xylogics[cntrlrPtr->controllerID] = xyPtr;
- xyPtr->magic = XY_CNTRLR_STATE_MAGIC;
- xyPtr->busy = FALSE;
- xyPtr->regsPtr = regsPtr;
- xyPtr->number = cntrlrPtr->controllerID;
- xyPtr->requestPtr = (Request *) NIL;
- /*
- * Allocate the mapped DMA memory for the IOPB. This memory should not
- * be freed unless the controller is not going to be accessed again.
- */
- addr = VmMach_DMAAlloc(sizeof(XylogicsIOPB), malloc(sizeof(XylogicsIOPB)));
- if (addr == (Address) NIL) {
- panic("DevXylogics450Init: unable to allocate DMA memory.\n");
- }
- xyPtr->IOPBPtr = (volatile XylogicsIOPB *) addr;
- /*
- * Initialize synchronization variables and set the controllers
- * state to alive and not busy.
- */
- Sync_SemInitDynamic(&xyPtr->mutex,"Dev:xylogics mutex");
- xyPtr->numSpecialWaiting = 0;
- xyPtr->ctrlQueues = Dev_CtrlQueuesCreate(&xyPtr->mutex, xyEntryAvailProc);
-
- for (i = 0 ; i < XYLOGICS_MAX_DISKS ; i++) {
- xyPtr->disks[i] = (XylogicsDisk *) NIL;
- }
- ResetController(regsPtr);
- return( (ClientData) xyPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ReleaseProc --
- *
- * Device release proc for controller.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static ReturnStatus
- ReleaseProc(handlePtr)
- DevBlockDeviceHandle *handlePtr; /* Handle pointer of device. */
- {
- PartitionDisk *pdiskPtr = (PartitionDisk *) handlePtr;
- free((char *) pdiskPtr);
- return SUCCESS;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * IOControlProc --
- *
- * Do a special operation on a raw SMD Disk.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- /*ARGSUSED*/
- static ReturnStatus
- IOControlProc(handlePtr, ioctlPtr, replyPtr)
- DevBlockDeviceHandle *handlePtr; /* Handle pointer of device. */
- Fs_IOCParam *ioctlPtr; /* Standard I/O Control parameter block */
- Fs_IOReply *replyPtr; /* Size of outBuffer and returned signal */
- {
-
- switch (ioctlPtr->command) {
- case IOC_REPOSITION:
- /*
- * Reposition is ok
- */
- return(SUCCESS);
- /*
- * No disk specific bits are set this way.
- */
- case IOC_GET_FLAGS:
- case IOC_SET_FLAGS:
- case IOC_SET_BITS:
- case IOC_CLEAR_BITS:
- return(SUCCESS);
-
- case IOC_GET_OWNER:
- case IOC_SET_OWNER:
- return(GEN_NOT_IMPLEMENTED);
-
- case IOC_TRUNCATE:
- return(GEN_INVALID_ARG);
-
- case IOC_LOCK:
- case IOC_UNLOCK:
- return(GEN_NOT_IMPLEMENTED);
-
- case IOC_NUM_READABLE:
- return(GEN_NOT_IMPLEMENTED);
-
- case IOC_MAP:
- return(GEN_NOT_IMPLEMENTED);
-
- default:
- return(GEN_INVALID_ARG);
- }
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * BlockIOProc --
- *
- * Start a block IO operations on a SMD disk attach to a xylogics
- * controller.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static ReturnStatus
- BlockIOProc(handlePtr, requestPtr)
- DevBlockDeviceHandle *handlePtr; /* Handle pointer of device. */
- DevBlockDeviceRequest *requestPtr; /* IO Request to be performed. */
- {
- PartitionDisk *pdiskPtr = (PartitionDisk *) handlePtr;
- register XylogicsDisk *diskPtr = pdiskPtr->diskPtr;
- register Request *reqPtr;
-
- reqPtr = (Request *) requestPtr->ctrlData;
- if (requestPtr->operation == FS_READ) {
- reqPtr->command = XY_READ;
- } else {
- reqPtr->command = XY_WRITE;
- }
- reqPtr->diskPtr = diskPtr;
- FillInDiskTransfer(pdiskPtr, requestPtr->startAddress,
- (unsigned) requestPtr->bufferLen,
- &(reqPtr->diskAddress), &(reqPtr->numSectors));
- reqPtr->buffer = requestPtr->buffer;
- reqPtr->retries = 0;
- reqPtr->requestPtr = requestPtr;
- Dev_QueueInsert(diskPtr->queue, (List_Links *) reqPtr);
- return SUCCESS;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * xyIdleCheck --
- *
- * Routine for the Disk Stats module to use to determine the idleness
- * for a disk.
- *
- * Results:
- * TRUE if the disk pointed to by clientData is idle, FALSE otherwise.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- /*ARGSUSED*/
- static Boolean
- xyIdleCheck(clientData, diskStatsPtr)
- ClientData clientData;
- DevDiskStats *diskStatsPtr; /* Unused for xylogics. */
- {
- XylogicsDisk *diskPtr = (XylogicsDisk *) clientData;
- return (!diskPtr->xyPtr->busy);
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * DevXylogics450DiskAttach --
- *
- * Initialize a device hanging off an Xylogics controller.
- *
- * Results:
- * A DevBlockDeviceHanlde for this disk.
- *
- * Side effects:
- * Disks: The label sector is read and the partitioning of
- * the disk is set up. The partitions correspond to device
- * files of the same type but with different unit number.
- *
- *----------------------------------------------------------------------
- */
- DevBlockDeviceHandle *
- DevXylogics450DiskAttach(devicePtr)
- Fs_Device *devicePtr; /* Device to attach. */
- {
- ReturnStatus error;
- XylogicsController *xyPtr; /* Xylogics specific controller state */
- register XylogicsDisk *diskPtr;
- PartitionDisk *pdiskPtr; /* Partitioned disk pointer. */
- int controllerID;
- int diskNumber;
-
- controllerID = XYLOGICS_CTRL_NUM_FROM_DEVUNIT(devicePtr->unit);
- diskNumber = XYLOGICS_DISK_NUM_FROM_DEVUNIT(devicePtr->unit);
- /* XXX */ printf("XyDiskAttach unit 0x%x ctrlr %d disk %d\n",
- devicePtr->unit, controllerID, diskNumber);
- xyPtr = xylogics[controllerID];
- if (xyPtr == (XylogicsController *)NIL ||
- xyPtr == (XylogicsController *)0 ||
- xyPtr->magic != XY_CNTRLR_STATE_MAGIC ||
- diskNumber > XYLOGICS_MAX_DISKS) {
- return ((DevBlockDeviceHandle *) NIL);
- }
- /*
- * Set up a slot in the disk list. We do a malloc before we grap the
- * MASTER_LOCK().
- */
- diskPtr = (XylogicsDisk *) malloc(sizeof(XylogicsDisk));
- bzero((char *) diskPtr, sizeof(XylogicsDisk));
- diskPtr->magic = XY_DISK_STATE_MAGIC;
- diskPtr->xyPtr = xyPtr;
- diskPtr->xyDriveType = 0;
- diskPtr->slaveID = diskNumber;
- diskPtr->queue = Dev_QueueCreate(xyPtr->ctrlQueues, 1,
- DEV_QUEUE_FIFO_INSERT, (ClientData) diskPtr);
-
- MASTER_LOCK(&(xyPtr->mutex));
- if (xyPtr->disks[diskNumber] == (XylogicsDisk *) NIL) {
- /*
- * See if the disk is really there.
- */
- xyPtr->disks[diskNumber] = diskPtr;
- error = TestDisk(xyPtr, diskPtr);
- if (error == SUCCESS) {
- /*
- * Look at the zero'th sector for disk information. This also
- * sets the drive type with the controller.
- */
- error = ReadDiskLabel(xyPtr, diskPtr);
- }
-
- if (error != SUCCESS) {
- xyPtr->disks[diskNumber] = (XylogicsDisk *) NIL;
- MASTER_UNLOCK(&(xyPtr->mutex));
- Dev_QueueDestroy(diskPtr->queue);
- free((Address)diskPtr);
- return ((DevBlockDeviceHandle *) NIL);
- }
- MASTER_UNLOCK(&(xyPtr->mutex));
- /*
- * Register the disk with the disk stats module.
- */
- {
- Fs_Device rawDevice;
- char name[128];
-
- rawDevice = *devicePtr;
- rawDevice.unit = rawDevice.unit & ~0xf;
- (void) sprintf(name, "xy%d-%d", xyPtr->number, diskPtr->slaveID);
- diskPtr->diskStatsPtr = DevRegisterDisk(&rawDevice, name,
- xyIdleCheck, (ClientData) diskPtr);
- }
- } else {
- /*
- * The disk already exists. Use it.
- */
- MASTER_UNLOCK(&(xyPtr->mutex));
- Dev_QueueDestroy(diskPtr->queue);
- free((Address)diskPtr);
- diskPtr = xyPtr->disks[diskNumber];
- }
- pdiskPtr = (PartitionDisk *) malloc(sizeof(*pdiskPtr));
- bzero((char *) pdiskPtr, sizeof(*pdiskPtr));
- pdiskPtr->handle.blockIOProc = BlockIOProc;
- pdiskPtr->handle.IOControlProc = IOControlProc;
- pdiskPtr->handle.releaseProc = ReleaseProc;
- pdiskPtr->handle.minTransferUnit = DEV_BYTES_PER_SECTOR;
- pdiskPtr->handle.maxTransferSize = FS_BLOCK_SIZE;
- pdiskPtr->partition = DISK_IS_PARTITIONED(devicePtr) ?
- DISK_PARTITION(devicePtr) :
- WHOLE_DISK_PARTITION;
- pdiskPtr->diskPtr = diskPtr;
- return ((DevBlockDeviceHandle *) pdiskPtr);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ResetController --
- *
- * Reset the controller. This is done by reading the reset/update
- * register of the controller.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Reset the controller.
- *
- *----------------------------------------------------------------------
- */
- static void
- ResetController(regsPtr)
- volatile XylogicsRegs *regsPtr;
- {
- char x;
- x = regsPtr->resetUpdate;
- #ifdef lint
- regsPtr->resetUpdate = x;
- #endif
- MACH_DELAY(100);
- return;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TestDisk --
- *
- * Get a drive's status to see if it exists.
- *
- * Results:
- * SUCCESS if the device is ok, DEV_OFFLINE otherwise.
- *
- * Side effects:
- * none.
- *
- *----------------------------------------------------------------------
- */
- static ReturnStatus
- TestDisk(xyPtr, diskPtr)
- XylogicsController *xyPtr;
- XylogicsDisk *diskPtr;
- {
- register ReturnStatus status;
- Request request;
-
- bzero((char *) &request, sizeof(request));
-
- request.command = XY_READ_STATUS;
- request.diskPtr = diskPtr;
-
- (void) SendCommand(diskPtr, &request, TRUE);
-
- #ifdef notdef
- printf("TestDisk:\n");
- printf("Xylogics-%d disk %d\n", xyPtr->number, diskPtr->slaveID);
- printf("Drive Status Byte %x\n", xyPtr->IOPBPtr->numSectLow);
- printf("Drive Type %d: Cyls %d Heads %d Sectors %d\n",
- diskPtr->xyDriveType,
- xyPtr->IOPBPtr->cylHigh << 8 | xyPtr->IOPBPtr->cylLow,
- xyPtr->IOPBPtr->head, xyPtr->IOPBPtr->sector);
- printf("Bytes Per Sector %d, Num sectors %d\n",
- xyPtr->IOPBPtr->dataAddrHigh << 8 |
- xyPtr->IOPBPtr->dataAddrLow,
- xyPtr->IOPBPtr->relocLow);
- MACH_DELAY(1000000);
- #endif notdef
- /*
- * If all the status bits are low then the drive is ok.
- */
- if (xyPtr->IOPBPtr->numSectLow == 0) {
- status = SUCCESS;
- } else if (xyPtr->IOPBPtr->numSectLow == 0x20) {
- printf("Warning: Xylogics-%d disk %d write protected\n",
- xyPtr->number, diskPtr->slaveID);
- status = SUCCESS;
- } else {
- status = DEV_OFFLINE;
- }
-
- return(status);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ReadDiskLabel --
- *
- * Read the label of the disk and record the partitioning info.
- *
- * This should also check the Drive Type written on sector zero
- * of cylinder zero. Use the Read Drive Status command for this.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Define the disk partitions that determine which part of the
- * disk each different disk device uses.
- *
- *----------------------------------------------------------------------
- */
- static ReturnStatus
- ReadDiskLabel(xyPtr, diskPtr)
- XylogicsController *xyPtr;
- XylogicsDisk *diskPtr;
- {
- register ReturnStatus error ;
- Sun_DiskLabel *diskLabelPtr;
- Dev_DiskAddr diskAddr;
- int part;
- Request request;
- XylogicsSectorHeader *headerPtr; /* Sector header info */
- char labelBuffer[DEV_BYTES_PER_SECTOR + 8]; /* Buffer for reading the
- * disk label into. The
- * buffer has 8 extra bytes
- * reading low-level sector
- * info. */
-
-
- /*
- * Do a low level read that includes sector header info and ecc codes.
- * This is done so we can learn the drive type needed in other commands.
- */
- diskAddr.head = 0;
- diskAddr.sector = 0;
- diskAddr.cylinder = 0;
-
- bzero((char *) &request, sizeof(request));
- request.command = XY_RAW_READ;
- request.diskPtr = diskPtr;
- request.diskAddress = diskAddr;
- request.numSectors = 1;
- request.buffer = labelBuffer;
-
- error = SendCommand(diskPtr, &request, TRUE);
- if (error != SUCCESS) {
- printf("Xylogics-%d: disk%d, couldn't read the label\n",
- xyPtr->number, diskPtr->slaveID);
- } else {
- /*XXX*/ printf("Header Bytes <%x, %x, %x, %x>\n", labelBuffer[0],
- labelBuffer[1], labelBuffer[2], labelBuffer[3]);
- headerPtr = (XylogicsSectorHeader *)labelBuffer;
- diskPtr->xyDriveType = headerPtr->driveType;
- diskLabelPtr = (Sun_DiskLabel *)(&labelBuffer[4]);
-
- printf("Label magic <%x>\n", diskLabelPtr->magic);
- printf("Drive type %x\n", diskPtr->xyDriveType);
- if (diskLabelPtr->magic == SUN_DISK_MAGIC) {
- printf("Xylogics-%d disk%d: %s\n", xyPtr->number, diskPtr->slaveID,
- diskLabelPtr->asciiLabel);
- diskPtr->numCylinders = diskLabelPtr->numCylinders;
- diskPtr->numHeads = diskLabelPtr->numHeads;
- diskPtr->numSectors = diskLabelPtr->numSectors;
-
- printf(" Partitions ");
- for (part = 0; part < DEV_NUM_DISK_PARTS; part++) {
- diskPtr->map[part].firstCylinder =
- diskLabelPtr->map[part].cylinder;
- diskPtr->map[part].numCylinders =
- diskLabelPtr->map[part].numBlocks /
- (diskLabelPtr->numHeads * diskLabelPtr->numSectors) ;
- printf(" (%d,%d)", diskPtr->map[part].firstCylinder,
- diskPtr->map[part].numCylinders);
- }
- printf("\n");
- /*
- * Now that we know what the disk is like, we have to make sure
- * that the controller does also. The set parameters command
- * sets the number of heads, sectors, and cylinders for the
- * drive type. The minus 1 is required because the controller
- * numbers from zero and these parameters are upper bounds.
- */
- diskAddr.head = diskPtr->numHeads - 1;
- diskAddr.sector = diskPtr->numSectors - 1;
- diskAddr.cylinder = diskPtr->numCylinders - 1;
-
- bzero((char *) &request, sizeof(request));
- request.command = XY_SET_DRIVE_SIZE;
- request.diskPtr = diskPtr;
- request.diskAddress = diskAddr;
-
- /* XXX */ printf("XY_SET_DRIVE_SIZE %d/%d/%d\n", diskAddr.cylinder, diskAddr.head, diskAddr.sector);
-
- error = SendCommand(diskPtr, &request, TRUE);
- if (error != SUCCESS) {
- printf("Xylogics-%d: disk%d, couldn't set drive size\n",
- xyPtr->number, diskPtr->slaveID);
- }
- } else {
- printf("Xylogics-%d Disk %d, Unsupported label, magic = <%x>\n",
- xyPtr->number, diskPtr->slaveID,
- diskLabelPtr->magic);
- error = FAILURE;
- }
- }
- return(error);
- }
-
-
- #if 0
- /*
- *----------------------------------------------------------------------
- *
- * VerifySectorHeader
- * Checks the low-level sector header information against the
- * logical address of the sector.
- *
- * Results:
- * Returns 1 if sector header is ok.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static Boolean
- VerifySectorHeader(diskAddrPtr, buffer)
- Dev_DiskAddr *diskAddrPtr; /* Disk disk address of
- * the first sector to transfer */
- char *buffer; /* Buffer with sector plus 8 bytes.
- * 4 bytes before, 4 bytes after. */
- {
- Dev_DiskAddr checkAddr;
- XylogicsSectorHeader *headerPtr;
-
- headerPtr = (XylogicsSectorHeader *)buffer;
- checkAddr.cylinder = (headerPtr->cylHigh << 8) | headerPtr->cylLow ;
- checkAddr.head = headerPtr->head;
- checkAddr.sector = (headerPtr->sectorHigh << 8) | headerPtr->sectorLow;;
-
- if (checkAddr.cylinder != diskAddrPtr->cylinder ||
- checkAddr.head != diskAddrPtr->head ||
- checkAddr.sector != diskAddrPtr->sector) {
- printf("XY: Bad Sector Header? bytes <");
- printf("%x %x %x %x", buffer[1], buffer[0], buffer[3], buffer[2]);
- printf(">");
- printf(" Logical Addr [%d,%d,%d]\n",
- diskAddrPtr->cylinder,
- diskAddrPtr->head,
- diskAddrPtr->sector);
- return 0;
- } else {
- return 1;
- }
- }
- #endif
-
-
- /*
- *----------------------------------------------------------------------
- *
- * FillInDiskTransfer
- * Fill in the disk address and number of sectors of a command.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The number of sectors to transfer gets trimmed down if it would
- * cross into the next partition.
- *
- *----------------------------------------------------------------------
- */
- static void
- FillInDiskTransfer(pdiskPtr, startAddress, length, diskAddrPtr, numSectorsPtr)
- PartitionDisk *pdiskPtr; /* Target Disk Partition. */
- unsigned int startAddress; /* Starting offset in bytes.*/
- unsigned int length; /* Length in bytes. */
- Dev_DiskAddr *diskAddrPtr; /* Disk disk address of
- * the first sector to transfer */
- int *numSectorsPtr; /* The number
- * of sectors to transferred. */
- {
- XylogicsDisk *diskPtr; /* State of the disk */
- int totalSectors; /* The total number of sectors to transfer */
- int lastSector; /* Last sector of the partition */
- int startSector; /* The first sector of the transfer */
- int part; /* Partition number. */
- int cylinderSize; /* Size of a cylinder in sectors. */
-
- diskPtr = pdiskPtr->diskPtr;
- part = pdiskPtr->partition;
- cylinderSize = diskPtr->numHeads * diskPtr->numSectors;
- /*
- * Do bounds checking to keep the I/O within the partition.
- * sectorZero is the sector number of the first sector in the partition,
- * lastSector is the sector number of the last sector in the partition.
- * (These sector numbers are relative to the start of the partition.)
- */
- lastSector = diskPtr->map[part].numCylinders * cylinderSize - 1;
- totalSectors = length/DEV_BYTES_PER_SECTOR;
-
- startSector = startAddress / DEV_BYTES_PER_SECTOR;
-
- if (startSector > lastSector) {
- /*
- * The offset is past the end of the partition.
- */
- *numSectorsPtr = 0;
- printf("Warning: XylogicsDiskIO: Past end of partition %d\n",
- part);
- return;
- } else if ((startSector + totalSectors - 1) > lastSector) {
- /*
- * The transfer is at the end of the partition. Reduce the
- * sector count so there is no overrun.
- */
- totalSectors = lastSector - startSector + 1;
- printf("Warning: XylogicsDiskIO: Overrun partition %d\n",
- part);
- }
- diskAddrPtr->cylinder = startSector / cylinderSize;
- startSector -= diskAddrPtr->cylinder * cylinderSize;
-
- diskAddrPtr->head = startSector / diskPtr->numSectors;
- startSector -= diskAddrPtr->head * diskPtr->numSectors;
-
- diskAddrPtr->sector = startSector;
-
- diskAddrPtr->cylinder += diskPtr->map[part].firstCylinder;
-
- if (diskAddrPtr->cylinder > diskPtr->numCylinders) {
- panic("Xylogics, bad cylinder # %d\n",
- diskAddrPtr->cylinder);
- }
- *numSectorsPtr = totalSectors;
- return;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * SetupIOPB --
- *
- * Setup a IOPB for a command. The IOPB can then
- * be passed to SendCommand. It specifies everything the
- * controller needs to know to do a transfer, and it is updated
- * with status information upon completion.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Set the various fields in the control block.
- *
- *----------------------------------------------------------------------
- */
- static void
- SetupIOPB(command, diskPtr, diskAddrPtr, numSectors, address, interrupt,IOPBPtr)
- char command; /* One of the Xylogics commands */
- XylogicsDisk *diskPtr; /* Spefifies unit, drive type, etc */
- register Dev_DiskAddr *diskAddrPtr; /* Head, sector, cylinder */
- int numSectors; /* Number of sectors to transfer */
- register Address address; /* Main memory address of the buffer */
- Boolean interrupt; /* If TRUE use interupts, else poll */
- register volatile XylogicsIOPB *IOPBPtr; /* I/O Parameter Block */
- {
- bzero((Address)IOPBPtr,sizeof(XylogicsIOPB));
-
- IOPBPtr->autoUpdate = 1;
- IOPBPtr->relocation = 1;
- IOPBPtr->doChaining = 0; /* New IOPB's are always end of chain */
- IOPBPtr->interrupt = interrupt;
- IOPBPtr->command = command;
-
- IOPBPtr->intrIOPB = interrupt; /* Polling or interrupt mode */
- IOPBPtr->autoSeekRetry = 1;
- IOPBPtr->enableExtras = 0;
- IOPBPtr->eccMode = 2; /* Correct soft errors for me, please */
-
- IOPBPtr->transferMode = 0; /* For words, not bytes */
- IOPBPtr->interleave = 0; /* For non interleaved */
- IOPBPtr->throttle = 4; /* max 32 words per burst */
-
- IOPBPtr->unit = diskPtr->slaveID;
- IOPBPtr->driveType = diskPtr->xyDriveType;
-
- if (diskAddrPtr != (Dev_DiskAddr *)NIL) {
- IOPBPtr->head = diskAddrPtr->head;
- IOPBPtr->sector = diskAddrPtr->sector;
- IOPBPtr->cylHigh = (diskAddrPtr->cylinder & 0x0700) >> 8;
- IOPBPtr->cylLow = (diskAddrPtr->cylinder & 0x00ff);
- }
-
- IOPBPtr->numSectHigh = (numSectors & 0xff00) >> 8;
- IOPBPtr->numSectLow = (numSectors & 0x00ff);
-
- if ((unsigned) address != 0 && (unsigned) address != (unsigned) NIL) {
- if ((unsigned) address < (unsigned) VMMACH_DMA_START_ADDR) {
- printf("%x: ", address);
- panic("Xylogics data address not in DMA space\n");
- }
- address = (Address)( (unsigned int)address - VMMACH_DMA_START_ADDR );
- IOPBPtr->relocHigh = ((int)address & 0xff000000) >> 24;
- IOPBPtr->relocLow = ((int)address & 0x00ff0000) >> 16;
- IOPBPtr->dataAddrHigh = ((int)address & 0x0000ff00) >> 8;
- IOPBPtr->dataAddrLow = ((int)address & 0x000000ff);
- }
- return;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * SendCommand --
- *
- * Lower level routine to read or write an Xylogics device. Use this
- * to transfer a contiguous set of sectors. The interface here
- * is in terms of a particular Xylogics disk and the command to be
- * sent. This routine takes care of mapping its
- * buffer into the special multibus memory area that is set up for
- * Sun DMA.
- *
- * Results:
- * An error code.
- *
- * Side effects:
- * Those of the command (Read, write etc.)
- *
- *----------------------------------------------------------------------
- */
- static ReturnStatus
- SendCommand(diskPtr, requestPtr, wait)
- XylogicsDisk *diskPtr; /* Unit, type, etc, of disk to send command. */
- Request *requestPtr; /* Command request block. */
- Boolean wait; /* Wait for the command to complete. */
- {
- XylogicsController *xyPtr; /* The Xylogics controller doing the command. */
- char command; /* One of the standard Xylogics commands */
- Dev_DiskAddr *diskAddrPtr; /* Head, sector, cylinder */
- int numSectors; /* Number of sectors to transfer */
- Address address; /* Main memory address of the buffer */
- ReturnStatus error;
- register volatile XylogicsRegs *regsPtr; /* I/O registers */
- unsigned int IOPBAddr;
- int retries = 0;
-
- xyPtr = diskPtr->xyPtr;
-
- if (xyPtr->busy) {
- if (wait) {
- while (xyPtr->busy) {
- xyPtr->numSpecialWaiting++;
- Sync_MasterWait(&xyPtr->specialCmdWait, &xyPtr->mutex, FALSE);
- xyPtr->numSpecialWaiting--;
- }
- } else {
- panic("Xylogics-%d: Software error, marked busy in SendCommand\n",
- xyPtr->number);
- }
- }
- xyPtr->busy = TRUE;
-
- command = requestPtr->command;
- diskAddrPtr = &requestPtr->diskAddress;
- address = requestPtr->buffer;
- numSectors = requestPtr->numSectors;
-
- /*
- * Without chaining the controller should always be idle at this
- * point.
- */
- regsPtr = xyPtr->regsPtr;
- if (regsPtr->status & XY_GO_BUSY) {
- printf("Warning: Xylogics waiting for busy controller\n");
- (void)WaitForCondition(regsPtr, XY_GO_BUSY);
- }
- /*
- * Set up the I/O registers for the transfer. All addresses given to
- * the controller must be relocated to the low megabyte so that the Sun
- * MMU will recognize them and map them back into the high megabyte of
- * the kernel's virtual address space. (As circular as this sounds,
- * the level of indirection means the system can use any physical page
- * for an I/O buffer.)
- */
- if ((unsigned int)xyPtr->IOPBPtr < VMMACH_DMA_START_ADDR) {
- printf("%x: ", xyPtr->IOPBPtr);
- panic("Warning: Xylogics IOPB not in DMA space\n");
- xyPtr->busy = FALSE;
- return(FAILURE);
- }
- IOPBAddr = (unsigned int)xyPtr->IOPBPtr - VMMACH_DMA_START_ADDR;
- regsPtr->relocHigh = (IOPBAddr & 0xFF000000) >> 24;
- regsPtr->relocLow = (IOPBAddr & 0x00FF0000) >> 16;
- regsPtr->addrHigh = (IOPBAddr & 0x0000FF00) >> 8;
- regsPtr->addrLow = (IOPBAddr & 0x000000FF);
-
- /*
- * If the command is going to transfer data be relocate the address
- * into DMA space.
- */
- if ((address != (Address) 0) && (numSectors > 0)) {
- if (command == XY_RAW_READ || command == XY_RAW_WRITE) {
- xyPtr->dmaBufferSize = (DEV_BYTES_PER_SECTOR + 8) * numSectors;
- } else {
- xyPtr->dmaBufferSize = DEV_BYTES_PER_SECTOR * numSectors;
- }
- xyPtr->dmaBuffer = VmMach_DMAAlloc(xyPtr->dmaBufferSize, address);
- if (xyPtr->dmaBuffer == (Address) NIL) {
- panic("SendCommand: unable to allocate DMA memory.");
- }
- } else {
- xyPtr->dmaBufferSize = 0;
- }
-
- retry:
- SetupIOPB(command, diskPtr, diskAddrPtr, numSectors, xyPtr->dmaBuffer,
- !wait, xyPtr->IOPBPtr);
- #ifdef notdef
- if (TRUE) {
- char *address;
- int i;
- printf("IOBP bytes: ");
- address = (char *)xyPtr->IOPBPtr;
- for (i=0 ; i<24 ; i++) {
- printf("%x ", address[i] & 0xff);
- }
- printf("\n");
- }
- #endif
- /*
- * Start up the controller
- */
- regsPtr->status = XY_GO_BUSY;
-
- if (wait) {
- /*
- * A synchronous command. Wait here for the command to complete.
- */
- error = WaitForCondition(regsPtr, XY_GO_BUSY);
- if (error != SUCCESS) {
- printf("Xylogics-%d: couldn't wait for command to complete\n",
- xyPtr->number);
- } else {
- /*
- * Check for error status from the operation itself.
- */
- error = GetStatus(xyPtr);
- if (error == DEV_RETRY_ERROR && retries < 3) {
- retries++;
- printf("Warning: Xylogics Retrying...\n");
- goto retry;
- }
- }
- if (xyPtr->dmaBufferSize > 0) {
- VmMach_DMAFree(xyPtr->dmaBufferSize, xyPtr->dmaBuffer);
- xyPtr->dmaBufferSize = 0;
- }
- xyPtr->busy = FALSE;
- StartNextRequest(xyPtr);
- } else {
- /*
- * Store request for interrupt handler.
- */
- xyPtr->requestPtr = requestPtr;
- error = SUCCESS;
- }
- return(error);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetStatus --
- *
- * Tidy up after a Xylogics command by looking at status bytes from
- * the device.
- *
- * Results:
- * An error code from the recently completed transfer.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static ReturnStatus
- GetStatus(xyPtr)
- XylogicsController *xyPtr;
- {
- register ReturnStatus error = SUCCESS;
- register volatile XylogicsRegs *regsPtr;
- register volatile XylogicsIOPB *IOPBPtr;
-
- regsPtr = xyPtr->regsPtr;
- IOPBPtr = xyPtr->IOPBPtr;
- if ((regsPtr->status & XY_ERROR) || IOPBPtr->error) {
- if (regsPtr->status & XY_DBL_ERROR) {
- printf("Xylogics-%d double error %x\n", xyPtr->number,
- IOPBPtr->errorCode);
- error = DEV_HARD_ERROR;
- } else {
- switch (IOPBPtr->errorCode) {
- case XY_NO_ERROR:
- error = SUCCESS;
- break;
- case XY_ERR_BAD_CYLINDER:
- printf("Xylogics bad cylinder # %d\n",
- IOPBPtr->cylHigh << 8 | IOPBPtr->cylLow);
- error = DEV_HARD_ERROR;
- break;
- case XY_ERR_BAD_SECTOR:
- printf("Xylogics bad sector # %d\n", IOPBPtr->sector);
- error = DEV_HARD_ERROR;
- break;
- case XY_ERR_BAD_HEAD:
- printf("Xylogics bad head # %d\n", IOPBPtr->head);
- error = DEV_HARD_ERROR;
- break;
- case XY_ERR_ZERO_COUNT:
- printf("Xylogics zero count\n");
- error = DEV_HARD_ERROR;
- break;
- case XY_ERR_INTR_PENDING:
- case XY_ERR_BUSY_CONFLICT:
- case XY_ERR_BAD_COMMAND:
- case XY_ERR_BAD_SECTOR_SIZE:
- case XY_ERR_SELF_TEST_A:
- case XY_ERR_SELF_TEST_B:
- case XY_ERR_SELF_TEST_C:
- case XY_ERR_SLIP_SECTOR:
- case XY_ERR_SLAVE_ACK:
- case XY_FORMAT_ERR_RUNT:
- case XY_FORMAT_ERR_BAD_SIZE:
- case XY_SOFT_ECC:
- panic("Stupid Xylogics error: 0x%x\n",
- IOPBPtr->errorCode);
- error = DEV_HARD_ERROR;
- break;
- case XY_SOFT_ERR_TIME_OUT:
- case XY_SOFT_ERR_BAD_HEADER:
- case XY_SOFT_ERR_ECC:
- case XY_SOFT_ERR_NOT_READY:
- case XY_SOFT_ERR_HEADER:
- case XY_SOFT_ERR_FAULT:
- case XY_SOFT_ERR_SEEK:
- error = DEV_RETRY_ERROR;
- printf("Warning: Retryable Xylogics error: 0x%x\n",
- IOPBPtr->errorCode);
- break;
- case XY_WRITE_PROTECT_ON:
- printf("Xylogics-%d: ", xyPtr->number);
- printf("Warning: Write protected\n");
- error = GEN_NO_PERMISSION;
- break;
- case XY_SOFT_ECC_RECOVERED:
- printf("Xylogics-%d: ", xyPtr->number);
- printf("Warning: Soft ECC error recovered\n");
- error = SUCCESS;
- break;
- default:
- error = DEV_HARD_ERROR;
- break;
- }
- }
- /*
- * Reset the error by writing a 1 to the XY_ERROR bit.
- */
- if (regsPtr->status & XY_ERROR) {
- regsPtr->status = XY_ERROR;
- }
- }
- return(error);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * WaitForCondition --
- *
- * Wait for the Xylogics controller to finish. This is done by
- * polling its GO_BUSY bit until it reads zero.
- *
- * Results:
- * SUCCESS if it completed before a threashold time limit,
- * DEV_TIMEOUT otherwise.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static ReturnStatus
- WaitForCondition(regsPtr, condition)
- volatile XylogicsRegs *regsPtr;
- int condition;
- {
- register int i;
- ReturnStatus status = DEV_TIMEOUT;
-
- for (i=0 ; i<XYLOGICS_WAIT_LENGTH ; i++) {
- if ((regsPtr->status & condition) == 0) {
- return(SUCCESS);
- } else if (regsPtr->status & XY_ERROR) {
- /*
- * Let GetStatus() figure out what happened
- */
- return(SUCCESS);
- }
- MACH_DELAY(10);
- }
- printf("Warning: Xylogics reset");
- ResetController(regsPtr);
- return(status);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * DevXylogics450Intr --
- *
- * Handle interrupts from the Xylogics controller. This routine
- * may start any queued requests.
- *
- * Results:
- * TRUE if an Xylogics controller was responsible for the interrupt
- * and this routine handled it.
- *
- * Side effects:
- * Usually a process is notified that an I/O has completed.
- *
- *----------------------------------------------------------------------
- */
- Boolean
- DevXylogics450Intr(clientData)
- ClientData clientData;
- {
- ReturnStatus error = SUCCESS;
- register XylogicsController *xyPtr;
- register volatile XylogicsRegs *regsPtr;
- register volatile XylogicsIOPB *IOPBPtr;
- Request *requestPtr;
-
- xyPtr = (XylogicsController *) clientData;
- regsPtr = xyPtr->regsPtr;
- if (regsPtr->status & XY_INTR_PENDING) {
- MASTER_LOCK(&(xyPtr->mutex));
- /*
- * Reset the pending interrupt by writing a 1 to the
- * INTR_PENDING bit of the status register.
- */
- regsPtr->status = XY_INTR_PENDING;
- IOPBPtr = xyPtr->IOPBPtr;
- requestPtr = xyPtr->requestPtr;
- /*
- * Release the DMA buffer if command mapped one.
- */
- if (xyPtr->dmaBufferSize > 0) {
- VmMach_DMAFree(xyPtr->dmaBufferSize, xyPtr->dmaBuffer);
- xyPtr->dmaBufferSize = 0;
- }
- if ((regsPtr->status & XY_ERROR) || IOPBPtr->error) {
- error = GetStatus(xyPtr);
- if (error == DEV_RETRY_ERROR) {
- requestPtr->retries++;
- if (requestPtr->retries < 3) {
- xyPtr->busy = FALSE;
- error = SendCommand(requestPtr->diskPtr, requestPtr,FALSE);
- if (error == SUCCESS) {
- MASTER_UNLOCK(&(xyPtr->mutex));
- return (TRUE);
- }
- }
- }
- }
- /*
- * For now there are no occasions where only part
- * of an I/O can complete.
- */
- xyPtr->busy = FALSE;
- RequestDone(requestPtr->diskPtr,requestPtr,error,requestPtr->numSectors);
- StartNextRequest(xyPtr);
- MASTER_UNLOCK(&(xyPtr->mutex));
- return(TRUE);
- }
- return(FALSE);
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * RequestDone --
- *
- * Mark a request as done by calling the request's doneProc. DMA memory
- * is released.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- RequestDone(diskPtr, requestPtr, status, numSectors)
- XylogicsDisk *diskPtr;
- Request *requestPtr;
- ReturnStatus status;
- int numSectors;
- {
- if (numSectors > 0) {
- if (requestPtr->requestPtr->operation == FS_READ) {
- diskPtr->diskStatsPtr->diskStats.diskReads += numSectors;
- } else {
- diskPtr->diskStatsPtr->diskStats.diskWrites += numSectors;
- }
- }
- MASTER_UNLOCK(&diskPtr->xyPtr->mutex);
- (requestPtr->requestPtr->doneProc)(requestPtr->requestPtr, status,
- numSectors*DEV_BYTES_PER_SECTOR);
- MASTER_LOCK(&diskPtr->xyPtr->mutex);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * StartNextRequest --
- *
- * Start the next request of a Xylogics450 controller.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static void
- StartNextRequest(xyPtr)
- XylogicsController *xyPtr;
- {
- ClientData clientData;
- List_Links *newRequestPtr;
- ReturnStatus status;
-
- /*
- * If the controller is busy, just return.
- */
- if (xyPtr->busy) {
- return;
- }
- /*
- * If a SPECIAL command is waiting wake up the process to do the command.
- */
- if (xyPtr->numSpecialWaiting > 0) {
- Sync_MasterBroadcast(&xyPtr->specialCmdWait);
- return;
- }
- /*
- * Otherwise process requests from the Queue.
- */
- newRequestPtr = Dev_QueueGetNextFromSet(xyPtr->ctrlQueues,
- DEV_QUEUE_ANY_QUEUE_MASK,&clientData);
- while (newRequestPtr != (List_Links *) NIL) {
- register Request *requestPtr;
- XylogicsDisk *diskPtr;
-
- requestPtr = (Request *) newRequestPtr;
- diskPtr = (XylogicsDisk *) clientData;
-
- status = SendCommand(diskPtr, requestPtr, FALSE);
- if (status != SUCCESS) {
- RequestDone(requestPtr->diskPtr, requestPtr, status, 0);
- newRequestPtr = Dev_QueueGetNextFromSet(xyPtr->ctrlQueues,
- DEV_QUEUE_ANY_QUEUE_MASK,&clientData);
- } else {
- break;
- }
- }
- return;
- }
-
-